<?php


namespace common\services;


use common\exception\CheckException;
use common\models\GoldDetail;
use common\models\Product;
use common\models\ProductOrder;
use common\models\User;

class ProductOrderService
{

    /**
     * 购买画材
     * @param User $user
     * @param int $goodId
     * @param int $buyNum
     * @param int $deliveryType 配送方式
     * @param string $address 邮寄地址
     * @return bool
     * @throws \Throwable
     */
    public static function createOrder(User $user, $goodId, $buyNum, $deliveryType, $address)
    {
        bcscale(6);
        $transaction = \Yii::$app->getDb()->beginTransaction();
        try {
            //查询产品售价
            $goodInfo = Product::find()
                ->select([
                    'id as good_id',
                    'title',
                    'desc',
                    'origin_price',
                    'price',
                    'stock_num',
                    'cover',
                ])
                ->where(['id' => $goodId])
                ->andWhere(['is_show' => Product::SHOW_STATUS_TRUE])
                ->asArray()
                ->one();

            if (empty($goodInfo) || empty($goodInfo['price'])) {
                throw new CheckException('', 43001);
            }

            if ($buyNum < 1) {
                //购买数量错误
                throw new CheckException('', 44001);
            }

            if (bccomp($buyNum, $goodInfo['stock_num']) == 1) {
                //库存不足
                throw new CheckException('', 44002);
            }

            //总价
            $buyPrice = bcmul($goodInfo['price'], $buyNum);

            if (bccomp($buyPrice, $user->amount) == 1) {
                //余额不足
                throw new CheckException('', 2000);
            }

            $goodDescInfo = [
                'good_cover' => $goodInfo['cover'],
                'good_name' => $goodInfo['title'],
                'good_desc' => $goodInfo['desc'],
                'good_price' => $goodInfo['price'],
                'good_origin_price' => $goodInfo['origin_price'],
            ];

            $beforeGold = $user->amount;

            $addressInfo = [
                'mobile' => $user->mobile,
                'name' => $user->real_name,
                'address' => $address
            ];


            //生成订单
            $order = new ProductOrder();
            $order->user_id = $user->id;
            $order->price = $goodInfo['price'];
            $order->total_price = $buyPrice;
            $order->num = $buyNum;
            $order->order_no = self::makeOrderNo();
            $order->product_name = $goodInfo['title'];
            $order->product_id = $goodId;
            $order->product_info = json_encode($goodDescInfo);
            $order->status = ProductOrder::STATUS_SUCCESS;
            $order->delivery_type = $deliveryType;
            $order->address_info = json_encode($addressInfo);

            //售价为0时，不用扣余额
            $subAmountRes = (bccomp($buyPrice, 0) === 0) ? true : $user->updateCounters(['amount' => -$buyPrice]);

            //减库存
            $subStockRes = Product::findOne($goodId)->updateCounters(['stock_num' => -$buyNum]);

            if ($order->save() && $subAmountRes && $subStockRes) {
                //金币明细
                $goldDetail = new GoldDetail();
                $goldDetail->gold = -$buyPrice;
                $goldDetail->user_id = $user->id;
                $goldDetail->type = GoldDetail::TYPE_BUY_PRODUCT;
                $goldDetail->before_action_gold = $beforeGold;
                $goldDetail->after_action_gold = $user->amount;
                $goldDetail->ref_id = $order->id;
                if ($goldDetail->save()) {
                    $transaction->commit();
                    return true;
                }
            }
            $transaction->rollBack();
            throw new CheckException('', 43002);
        } catch (\Throwable $throwable) {
            $transaction->rollBack();
            throw $throwable;
        }
        return false;
    }

    /**
     * 订单号
     * @param int $moreCharNum
     * @return string
     */
    private static function makeOrderNo($moreCharNum = 2)
    {
        $endStr = '';
        for ($i = 1; $i <= $moreCharNum; $i++) {
            $endStr .= chr(rand(65, 90));
        }
        list($usec, $sec) = explode(" ", microtime());
        return 'P' . date('ymdHis', $sec) . substr($usec, 2, 6) . $endStr;

    }
}